package com.sqi.reactive.common.repository

import org.springframework.data.r2dbc.convert.R2dbcConverter
import org.springframework.data.r2dbc.core.DatabaseClient
import org.springframework.data.r2dbc.core.ReactiveDataAccessStrategy
import org.springframework.data.r2dbc.repository.support.R2dbcRepositoryFactory
import org.springframework.data.r2dbc.repository.support.R2dbcRepositoryFactoryBean
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity
import org.springframework.data.relational.repository.support.MappingRelationalEntityInformation
import org.springframework.data.repository.Repository
import org.springframework.data.repository.core.RepositoryInformation
import org.springframework.data.repository.core.RepositoryMetadata
import org.springframework.data.repository.core.support.RepositoryFactorySupport
import java.io.Serializable


/**
 * @author sjl
 * @date 2020/5/30
 */
open class DynamicQueryRepositoryFactoryBean<T : Repository<S, ID>, S, ID : Serializable>(
        repositoryInterface: Class<T>)
    : R2dbcRepositoryFactoryBean<T, S, ID>(repositoryInterface) {
    override fun getFactoryInstance(client: DatabaseClient, dataAccessStrategy: ReactiveDataAccessStrategy):
            RepositoryFactorySupport = DynamicQueryRepositoryFactory(client, dataAccessStrategy)

}

class DynamicQueryRepositoryFactory(private val databaseClient: DatabaseClient,
                                    private val dataAccessStrategy: ReactiveDataAccessStrategy
) : R2dbcRepositoryFactory(databaseClient, dataAccessStrategy) {
    private val mappingContext = dataAccessStrategy.converter.mappingContext
    private val converter: R2dbcConverter = dataAccessStrategy.converter

    override fun getRepositoryBaseClass(metadata: RepositoryMetadata): Class<*> = DynamicQueryRepositoryImpl::class.java

    @Suppress("UNCHECKED_CAST")
    override fun getTargetRepository(information: RepositoryInformation): Any =
            getTargetRepositoryViaReflection(information,
                    MappingRelationalEntityInformation<Any?, Any?>(
                            this.mappingContext.getRequiredPersistentEntity(information.domainType) as RelationalPersistentEntity<Any?>
                    ),
                    this.databaseClient, this.converter, this.dataAccessStrategy)
}